#include "Transaction.hh"
#include "BankSystem.hh"
#include "IAccount.hh"
#include "Customer.hh"
#include "AccountUtils.hh"
#include "IO.hh" 
#include <iostream>
#include <iomanip>
#include <sstream>

namespace Bank {
    int Transaction::transactionCounter = 0;
    //Generate a unique transaction ID, such as T001, T002, to identify each transaction.
    std::string Transaction::generateUniqueTransactionID() {
        std::ostringstream oss;
        oss << "T" << std::setw(3) << std::setfill('0') << ++transactionCounter;
        return oss.str();}

    Transaction::Transaction(const std::string& id, TransactionType type, double amount,
                             const std::string& srcAccId, const std::string& dstAccId, const std::string& note)
        : transactionId(id), type(type), amount(amount), sourceAccountId(srcAccId), 
          destinationAccountId(dstAccId), note(note), timestamp(getCurrentTimestamp()) {}

    std::string Transaction::getCurrentTimestamp() const {
        std::time_t now = std::time(nullptr);
        std::tm localTime;
        localtime_s(&localTime, &now);
        char buf[20];
        std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &localTime);
        return buf;}

    std::string Transaction::toString() const {
        std::ostringstream oss;
        oss << "Transaction ID: " << transactionId << "\n";
        oss << "[" << timestamp << "] ";
        switch (type) {
            case TransactionType::Deposit:
                oss << "Deposit into " << sourceAccountId << ": +(CNY)" << std::fixed << std::setprecision(2) << amount;
                break;
            case TransactionType::Withdraw:
                oss << "Withdraw from " << sourceAccountId << ": -(CNY)" << std::fixed << std::setprecision(2) << amount;
                break;
            case TransactionType::Transfer:
                oss << "Transfer from " << sourceAccountId << " to " << destinationAccountId << ": (CNY)" << std::fixed << std::setprecision(2) << amount;
                break;}
        if (!note.empty()) {//If there are additional notes, include them in the output.
            oss << " | Note: " << note;
        }
        return oss.str();}

    void Transaction::display() const {
        std::cout << toString() << std::endl;}

    std::string Transaction::getId() const {
        return transactionId;}

    std::string Transaction::getSourceAccountId() const {
        return sourceAccountId;}

    std::string Transaction::getDestinationAccountId() const {
        return destinationAccountId;}

    TransactionType Transaction::getType() const {
        return type;}

    double Transaction::getAmount() const {
        return amount;}

    std::string Transaction::getNote() const {
        return note;}

    std::string Transaction::getTimestamp() const {
        return timestamp;}

    bool Transaction::involvesCustomer(const std::shared_ptr<Customer>& customer) const {
        return customer->getSavingsAccount()->getAccountId() == sourceAccountId || 
               customer->getSavingsAccount()->getAccountId() == destinationAccountId || 
               customer->getCheckingAccount()->getAccountId() == sourceAccountId || 
               customer->getCheckingAccount()->getAccountId() == destinationAccountId;}

    bool Transaction::involvesAccount(const std::string& accountId) const {
        return sourceAccountId == accountId || destinationAccountId == accountId;}

    bool Transaction::performTransfer(BankSystem& bank, const std::shared_ptr<IAccount>& from, const std::string& toAccountId, double amount, const std::string& note) {
        auto to = bank.findAccountById(toAccountId);
        auto owner = bank.findCustomer(from->getOwnerName());
        if (!to || !owner) {//Invoke the transfer logic, and if successful, create a transaction record and add it to the system.
            std::cout << "Transfer failed. Invalid account or owner.\n";
            return false;}

        if (from->getAccountType() == "Checking" &&
            owner->getCheckingAccount()->getAccountId() == toAccountId) {
            std::cout << "Transfer failed. Cannot transfer to your own Checking Account.\n";
            return false;}

        std::cout << "\n===== Transfer from Account: " << from->getAccountId()<< " =====\nCurrent Balance: (CNY)" << std::fixed << std::setprecision(2) << from->getBalance() << "\n";

        if (!from->transferTo(to.get(), amount, note)) {
            std::cout << "Transfer failed. Check balance or limits.\n";
            return false;}

        Transaction tx(Transaction::generateUniqueTransactionID(), TransactionType::Transfer, amount, from->getAccountId(), toAccountId, note);
        bank.recordTransaction(tx);
        std::cout << "Transfer successful!\nNew balance: (CNY)" << std::fixed << std::setprecision(2) << from->getBalance() << "\n";
        std::cout << "Transaction ID: " << tx.getId() << " has been recorded.\n";

        std::string input;
        while (true) {
            std::cout << "Continue? (y/n): ";
            input = IO::getString();
            if (input == "y" || input == "Y") break;
            if (input == "n" || input == "N") {
                std::cout << "Staying on current page.\n";
                return false;}
            std::cout << "Invalid input. Please enter 'y' or 'n'.\n";}
        return true;}
}